package zdream.pmfield.test.stage.effect;

import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import zdream.pmfield.core.Result;
import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.data.foe.VStat;
import zdream.pmfield.stage.StageBuilderForTest;
import zdream.pmfield.stage.StageWrapper;
import zdream.pmfield.stage.action.AttackAction;
import zdream.pmfield.stage.action.AttackReport;
import zdream.pmfield.stage.action.AttackReport.AttackReportItem;
import zdream.pmfield.stage.message.ActionRequestMessage;
import zdream.pmfield.stage.message.ActionStartMessage;
import zdream.pmfield.stage.message.AttackMessage;
import zdream.pmfield.stage.message.FoeAddBuffMessage;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.TurnFinishedMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.storage.FoeTemplateStorage;
import zdream.pmfield.test.utils.ResourceConstant;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-12-05
 * @since 0.0.1
 */
public class DrowsyBuffCase {
	FoeTemplateStorage storage;

	@Before
	public void before() {
		storage = new FoeTemplateStorage();
		storage.load();
		storage.loadMovesInResource(ResourceConstant.RES_PATH_TEST_MOVES_JSON);
	}

	@Test
	public void testDrowsyEffect() {
		MoveTemplate drowsyMove = storage.findMoveById(10000023, 1);
		assertEquals("肯定瞌睡", drowsyMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(4))
				.withLevel(30)
				.withMove(drowsyMove)
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(1))
				.withLevel(30)
				.withMove(normalMove)
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: apply drowsy");
		AtomicBoolean drowsyRequest = new AtomicBoolean(false);
		AtomicBoolean drowsyApplied = new AtomicBoolean(false);
		AtomicBoolean checked = new AtomicBoolean(false);

		wrapper.printBeforeHandleMessage();
		wrapper.onActionRequestForAll(wrapper.createDefaultController().withActionChecker(action -> {
			if (action.move.move == drowsyMove) {
				drowsyRequest.set(true);
			}
		}));
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			assertTrue(message.buff.is(StageConstant.EFFECT_DROWSY));
			assertTrue(message.success);
			drowsyApplied.set(true);
		});
		wrapper.onMessage(TurnFinishedMessage.class, message -> {
			if (!drowsyRequest.get()) {
				return;
			}
			assertTrue(drowsyApplied.get());
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 2: drowsy 状态: 每回合开始时判定, 失败就跳过回合");

		AtomicInteger failToMoveCount = new AtomicInteger(0);
		checked.set(false);

		AtomicInteger targetTurnCount = new AtomicInteger(0);
		AtomicInteger drowsyCheckCount = new AtomicInteger(0);

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.onMessage(TurnStartMessage.class, message -> {
			final FoeActor foe = message.foe;
			if (foe.foe != foe2) {
				return;
			}
			targetTurnCount.incrementAndGet();
			assertEquals(0, failToMoveCount.get());
		});
		wrapper.onMessage(RollReportMessage.class, message -> {
			final FoeActor foe = message.foe;
			if (foe.foe != foe2) {
				return;
			}
			if (!StageConstant.BUFF_DROWSY.equals(message.title)) {
				return;
			}
			drowsyCheckCount.incrementAndGet();
			assertEquals(targetTurnCount.get(), drowsyCheckCount.get());

			// 补充判定, roll 的判定是基础数值 -20
			assertEquals(2, message.rolls.length);
			assertEquals(foe.vStats[VStat.LUCK.ordinal()] - 20, message.rolls[0].baseValue);
			assertEquals(foe.vStats[VStat.LUCK.ordinal()] - 20, message.rolls[0].fixedBaseValue);
			assertEquals(foe.vStats[VStat.LUCK.ordinal()] - 20, message.rolls[1].baseValue);
			assertEquals(foe.vStats[VStat.LUCK.ordinal()] - 20, message.rolls[1].fixedBaseValue);

			long successCount = Arrays.stream(message.rolls)
					.filter(roll -> roll.result)
					.count();
			if (successCount == 0) {
				failToMoveCount.incrementAndGet();
			}
		});
		wrapper.onMessage(ActionStartMessage.class, message -> {
			if (failToMoveCount.get() == 0) {
				return;
			}
			assertTrue(message.action.isForDaze());
			assertEquals(foe2, message.action.asDaze().foe.foe);
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 3: drowsy 状态会随时间消失, 需要三次 drowsy 行动判定失败");
		// 也就是 failToMoveCount.get() == 3

		wrapper.clearOnMessageHooks();
		checked.set(false);

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.onMessage(TurnStartMessage.class, message -> {
			final FoeActor foe = message.foe;
			if (foe.foe != foe2) {
				return;
			}
			assertTrue(failToMoveCount.get() <= 3);
		});
		wrapper.onMessage(RollReportMessage.class, message -> {
			final FoeActor foe = message.foe;
			if (foe.foe != foe2) {
				return;
			}
			if (!StageConstant.BUFF_DROWSY.equals(message.title)) {
				return;
			}
			long successCount = Arrays.stream(message.rolls)
					.filter(roll -> roll.result)
					.count();
			if (successCount == 0) {
				failToMoveCount.incrementAndGet();
			}
		});
		wrapper.onMessage(FoeRemoveBuffMessage.class, message -> {
			assertEquals(foe2, message.foe.foe);
			assertTrue(message.buff.is(StageConstant.BUFF_DROWSY));
			assertEquals(3, failToMoveCount.get());
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());
	}

	@Test
	public void testDrowsyIncreaseDamage() {
		MoveTemplate drowsyMove = storage.findMoveById(10000023, 1);
		assertEquals("肯定瞌睡", drowsyMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);
		MoveTemplate attackMove = storage.findMoveById(10000020, 1);
		assertEquals("物理的", attackMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(4))
				.withLevel(30)
				.withMove(drowsyMove)
				.withMove(attackMove)
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(1))
				.withLevel(30)
				.withMove(normalMove)
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: apply drowsy");
		AtomicBoolean checked = new AtomicBoolean(false);

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest();
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			assertTrue(message.buff.is(StageConstant.BUFF_DROWSY));
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 2: 打一架试试伤害有没有升上来");
		wrapper.runUntil(TurnFinishedMessage.class);
		checked.set(false);

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest(foe2);
		wrapper.onMessage(ActionRequestMessage.class, message -> {
			if (message.foe.foe != foe1) {
				return;
			}
			final Result result = message.createReplyTemplate()
					.withMove(attackMove)
					.withTarget(foe2)
					.commit();
			assertTrue(result.errorMessage, result.success);
		});
		wrapper.onMessage(AttackMessage.class, message -> {
			final AttackReport report = message.report;
			assertTrue(report.action.isForAttack());

			final AttackAction action = report.action.asAttack();
			if (action.foe.foe != foe1) {
				return;
			}

			final List<AttackReportItem> items = report.items;
			assertEquals(1, items.size());
			assertEquals(0.8f, items.get(0).defenseFixed, 0.00001);
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());
	}
}
